use std::mem::size_of;
use std::{cell::Cell, marker::PhantomData};

use serde::{Deserialize, Serialize};

use super::AccessSpecifier;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShaderStorageBuffer {
    pub buffer: Cell<u32>,
    pub binding_point: Cell<u32>,
    pub size: Cell<usize>,
}

impl ShaderStorageBuffer {
    pub fn new(value: AccessSpecifier) -> Self {
        unsafe {
            let mut buffer = 0;
            gl::CreateBuffers(1, &mut buffer);
            gl::NamedBufferStorage(
                buffer,
                0 as _,
                std::ptr::null() as _,
                gl::DYNAMIC_STORAGE_BIT,
            );
            Self {
                buffer: Cell::new(buffer),
                binding_point: Cell::new(0),
                size: Cell::new(0),
            }
        }
    }
    pub fn bind(&self, point: u32) {
        unsafe {
            self.binding_point.set(point);
            gl::BindBufferBase(gl::SHADER_STORAGE_BUFFER, point, self.buffer.get());
        }
    }

    pub fn unbind(&self) {
        unsafe {
            let point = self.binding_point.take();
            gl::BindBufferBase(gl::SHADER_STORAGE_BUFFER, point, 0);
        }
    }

    pub fn buffer(&self) -> u32 {
        self.buffer.get()
    }

    pub fn send_blocks<T>(&self, value: &[T])
    where
        T: bytemuck::Pod,
    {
        unsafe {
            let data: &[u8] = bytemuck::cast_slice(value);

            let len = self.size.get();
            if data.len() != len {
                let mut buffer = self.buffer.get();
                gl::DeleteBuffers(1, &buffer);

                gl::CreateBuffers(1, &mut buffer);
                gl::NamedBufferStorage(
                    buffer,
                    data.len() as _,
                    std::ptr::null() as _,
                    gl::DYNAMIC_STORAGE_BIT,
                );
                self.size.set(data.len());
                self.buffer.set(buffer);
                self.bind(0);
            }

            gl::NamedBufferSubData(self.buffer.get(), 0, data.len() as _, data.as_ptr() as _);
        }
    }
}
